package ck_log

import (
	"encoding/json"
	"fmt"
	sls "github.com/aliyun/aliyun-log-go-sdk"
	"github.com/aliyun/aliyun-log-go-sdk/producer"
	"github.com/spf13/viper"
	"strconv"
	"sync"
	"time"
)

type LtvWriter struct {
	sec  bool
	size int
	mu   sync.Mutex
}

func (l *LtvWriter) Write(p []byte) (n int, err error) {
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.sec {
		if producerSecInstance == nil {
			InitSecProducer()
		}
		// log := producer.GenerateLog(uint32(time.Now().Unix()), map[string]string{"content": string(p)})
		log := producer.GenerateLog(uint32(time.Now().Unix()), byteToMap(p))
		produceSecChan <- log

	} else {
		if producerInstance == nil {
			InitProducer()
		}
		// GenerateLog  is producer's function for generating SLS format logs
		// GenerateLog has low performance, and native Log interface is the best choice for high performance.
		// log := producer.GenerateLog(uint32(time.Now().Unix()), map[string]string{"content": string(p)})
		log := producer.GenerateLog(uint32(time.Now().Unix()), byteToMap(p))
		produceChan <- log
	}
	n = len(p)
	l.size += n
	// n = l.size
	return
}

// Access Key 配置
var (
	// 日志LogStore
	store               = ""
	project             = ""
	producerInstance    *producer.Producer
	produceChan         chan *sls.Log
	producerSecInstance *producer.Producer
	produceSecChan      chan *sls.Log
)

// Init 初始化
func InitProducer() {
	store = viper.GetString("aliyun.log.store")
	project = viper.GetString("aliyun.log.project")
	producerConfig := producer.GetDefaultProducerConfig()
	producerConfig.Endpoint = "cn-shanghai.log.aliyuncs.com"
	producerConfig.AccessKeyID = viper.GetString("aliyun.log.key")
	producerConfig.AccessKeySecret = viper.GetString("aliyun.log.secret")
	producerInstance = producer.InitProducer(producerConfig)
	producerInstance.Start()

	produceChan = make(chan *sls.Log, 1000)
	go func() {
		for {
			select {
			case item := <-produceChan:
				errs := producerInstance.SendLog(project, store, "topic", "127.0.0.1", item)
				if errs != nil {
					fmt.Println(errs)
				}
			}
		}
	}()
}

func InitSecProducer() {
	store = viper.GetString("aliyun.seclog.store")
	project = viper.GetString("aliyun.seclog.project")
	producerConfig := producer.GetDefaultProducerConfig()
	producerConfig.Endpoint = "cn-shanghai.log.aliyuncs.com"
	producerConfig.AccessKeyID = viper.GetString("aliyun.seclog.key")
	producerConfig.AccessKeySecret = viper.GetString("aliyun.seclog.secret")
	producerSecInstance = producer.InitProducer(producerConfig)
	producerSecInstance.Start()

	produceSecChan = make(chan *sls.Log, 1000)
	go func() {
		for {
			select {
			case item := <-produceSecChan:
				errs := producerSecInstance.SendLog(project, store, "topic", "127.0.0.1", item)
				if errs != nil {
					fmt.Println(errs)
				}
			}
		}
	}()
}

func byteToMap(p []byte) map[string]string {
	res := map[string]interface{}{}
	err := json.Unmarshal(p, &res)
	if err != nil {
		fmt.Println(err, string(p))
		return map[string]string{
			"json_err": err.Error(),
		}
	}

	re := map[string]string{}
	for k, v := range res {
		re[k] = interfaceToStr(v)
	}
	return re
}

// InterfaceToStr 任意类型转string
func interfaceToStr(value interface{}) string {
	var key string
	if value == nil {
		return key
	}

	switch value.(type) {
	case float64:
		ft := value.(float64)
		key = strconv.FormatFloat(ft, 'f', -1, 64)
	case float32:
		ft := value.(float32)
		key = strconv.FormatFloat(float64(ft), 'f', -1, 64)
	case int:
		it := value.(int)
		key = strconv.Itoa(it)
	case uint:
		it := value.(uint)
		key = strconv.Itoa(int(it))
	case int8:
		it := value.(int8)
		key = strconv.Itoa(int(it))
	case uint8:
		it := value.(uint8)
		key = strconv.Itoa(int(it))
	case int16:
		it := value.(int16)
		key = strconv.Itoa(int(it))
	case uint16:
		it := value.(uint16)
		key = strconv.Itoa(int(it))
	case int32:
		it := value.(int32)
		key = strconv.Itoa(int(it))
	case uint32:
		it := value.(uint32)
		key = strconv.Itoa(int(it))
	case int64:
		it := value.(int64)
		key = strconv.FormatInt(it, 10)
	case uint64:
		it := value.(uint64)
		key = strconv.FormatUint(it, 10)
	case string:
		key = value.(string)
	case []byte:
		key = string(value.([]byte))
	default:
		newValue, _ := json.Marshal(value)
		key = string(newValue)
	}

	return key
}
